!theme mono

skinparam sequenceArrowThickness 2
skinparam sequenceParticipantBorderThickness 2
skinparam sequenceActorBorderThickness 2
skinparam sequenceGroupBorderThickness 2

title User Service 내부 시퀀스 다이어그램 (역설계 - 개발 소스 기반)

participant "AuthController" as AuthCtrl
participant "UserController" as UserCtrl
participant "AuthUseCase" as AuthUC
participant "UserUseCase" as UserUC
participant "AuthDomainService" as AuthDomainSvc
participant "UserDomainService" as UserDomainSvc
participant "UserRepository" as UserRepo
participant "OccupationRepository" as OccupationRepo
participant "GoogleAuthAdapter" as AuthACL
participant "JwtTokenAdapter" as JwtAdapter
participant "EventPublisherAdapter" as EventPub
participant "PostgreSQL" as DB
participant "Google SSO" as GoogleSSO
participant "Azure Service Bus" as ServiceBus

== 1. POST /api/users/auth/google-login (구글 로그인) ==

AuthCtrl -> AuthUC: authenticateWithGoogle(googleLoginRequest)
note right: {googleAccessToken, googleIdToken}

AuthUC -> AuthDomainSvc: validateGoogleTokens(accessToken, idToken)
AuthDomainSvc -> AuthACL: verifyGoogleToken(accessToken, idToken)
AuthACL -> GoogleSSO: Google Token Verification API
GoogleSSO -> AuthACL: Verified User Info
AuthACL -> AuthDomainSvc: GoogleUserInfo 응답

AuthDomainSvc -> UserRepo: findByGoogleId(googleId)
UserRepo -> DB: SELECT * FROM users WHERE google_id = ?
DB -> UserRepo: User 엔티티 또는 null

alt 신규 사용자인 경우
    AuthDomainSvc -> AuthDomainSvc: createNewUser(googleUserInfo)
    AuthDomainSvc -> UserRepo: saveUser(newUser)
    UserRepo -> DB: INSERT INTO users
    DB -> UserRepo: 저장 완료
    
    AuthUC -> EventPub: publishUserRegisteredEvent(user)
    EventPub -> ServiceBus: 신규 사용자 등록 이벤트 발행
end

AuthUC -> JwtAdapter: generateTokens(user)
JwtAdapter -> AuthUC: {accessToken, refreshToken}

AuthUC -> UserRepo: updateLastLoginAt(user.id)
UserRepo -> DB: UPDATE users SET last_login_at = NOW()
DB -> UserRepo: 업데이트 완료

AuthUC -> AuthCtrl: LoginResponse 반환
note right: {accessToken, refreshToken, userId, isNewUser, message}

== 2. POST /api/users/profile/complete (프로필 완료) ==

AuthCtrl -> UserUC: completeUserProfile(userRegistrationRequest)
note right: JWT에서 추출한 userId + {name, birthDate, occupation}

UserUC -> UserDomainSvc: validateProfileData(profileData)
UserDomainSvc -> OccupationRepo: validateOccupationCode(occupation)
OccupationRepo -> DB: SELECT * FROM occupation_types WHERE occupation_code = ?
DB -> OccupationRepo: Occupation 엔티티

UserDomainSvc -> UserRepo: findById(userId)
UserRepo -> DB: SELECT * FROM users WHERE id = ?
DB -> UserRepo: User 엔티티

UserDomainSvc -> UserDomainSvc: updateUserProfile(user, profileData)
UserDomainSvc -> UserRepo: updateUser(updatedUser)
UserRepo -> DB: UPDATE users SET name = ?, birth_date = ?, occupation = ?, updated_at = NOW()
DB -> UserRepo: 업데이트 완료

UserUC -> EventPub: publishUserProfileUpdatedEvent(user)
EventPub -> ServiceBus: 회원정보 업데이트 이벤트 발행

UserUC -> AuthCtrl: UserRegistrationResponse 반환
note right: {userId, message, status, profileCompletedAt}

== 3. GET /api/users/profile (사용자 기본 정보 조회) ==

AuthCtrl -> UserUC: getUserProfile(userId)
note right: JWT에서 추출한 userId

UserUC -> UserRepo: findById(userId)
UserRepo -> DB: SELECT * FROM users WHERE id = ?
DB -> UserRepo: User 엔티티

UserUC -> UserDomainSvc: calculateAge(user.birthDate)
note right: 생년월일로부터 나이 계산

UserUC -> OccupationRepo: findOccupationName(user.occupation)
OccupationRepo -> DB: SELECT occupation_name FROM occupation_types WHERE occupation_code = ?
DB -> OccupationRepo: 직업명

UserUC -> AuthCtrl: UserProfileResponse 반환
note right: {userId, name, age, occupation, registeredAt, lastLoginAt}

== 4. GET /api/users/occupations (직업 목록 조회) ==

UserCtrl -> UserUC: getAllOccupations()

UserUC -> OccupationRepo: findAllOccupations()
OccupationRepo -> DB: SELECT * FROM occupation_types ORDER BY category, occupation_name
DB -> OccupationRepo: Occupation 목록

UserUC -> UserCtrl: OccupationListResponse 반환
note right: {occupations: [{occupationCode, occupationName, category}], totalCount}

== 5. GET /api/users/occupations/name (직업명 조회) ==

UserCtrl -> UserUC: getOccupationName(occupationCode)

UserUC -> OccupationRepo: findOccupationByCode(occupationCode)
OccupationRepo -> DB: SELECT * FROM occupation_types WHERE occupation_code = ?
DB -> OccupationRepo: Occupation 엔티티

UserUC -> UserCtrl: OccupationNameResponse 반환
note right: {occupationCode, occupationName}

== 6. GET /api/users/occupations/code (직업코드 조회) ==

UserCtrl -> UserUC: getOccupationCode(occupationName)

UserUC -> OccupationRepo: findOccupationByName(occupationName)
OccupationRepo -> DB: SELECT * FROM occupation_types WHERE occupation_name = ?
DB -> OccupationRepo: Occupation 엔티티

UserUC -> UserCtrl: OccupationCodeResponse 반환
note right: {occupationCode, occupationName}

== 예외 처리 ==

alt 인증 실패 시
    AuthACL -> AuthUC: AuthenticationException
    AuthUC -> AuthCtrl: 401 Unauthorized 응답
end

alt 사용자 정보 없음
    UserRepo -> UserUC: UserNotFoundException
    UserUC -> AuthCtrl: 404 Not Found 응답
end

alt 입력 데이터 검증 실패
    UserDomainSvc -> UserUC: ValidationException
    UserUC -> AuthCtrl: 400 Bad Request 응답
    note right: 유효성 검증 오류 메시지 포함
end

alt 직업 코드 없음
    OccupationRepo -> UserUC: OccupationNotFoundException
    UserUC -> UserCtrl: 404 Not Found 응답
end

== 트랜잭션 처리 ==

note over UserUC, UserRepo
**트랜잭션 범위**
- 사용자 생성/수정 작업
- 이벤트 발행은 트랜잭션 커밋 후
- @Transactional 어노테이션 적용
end note

== 보안 처리 ==

note over AuthUC, AuthACL
**보안 고려사항**
- JWT 토큰 생성 시 적절한 만료시간 설정
- Google SSO 응답 데이터 검증
- 개인정보 로깅 제외
- 비밀번호 없이 OAuth만 사용
end note